package dLogistic.domain.service;

// możliwość wstrzyknięcia do wyznaczania trasy paczki

// jedna z opcji jak może być wyznaczana trasa
// nie wykorzystywana w chwili obecnej w projekcie
//
// wyznaczenie odszukiwałoby najkrótszą pod względem odległości ścieżkę
// chociaż można zastąpić "wagę krawędzi" dowolnym pojęciem i np. waga
// to nie odległość, ale czas na pokonanie odcinak

import dLogistic.infrastructure.persistence.hibernate.HibernateUtil;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import java.util.*;

class DirectedEdge {
    private final int from;
    private final int to;
    private final long weight;

    public DirectedEdge(int from, int to, int weight) {
        this.from = from;
        this.to = to;
        this.weight = weight;
    }

    public int from() {
        return from;
    }

    public int to() {
        return to;
    }

    public long getWeight() {
        return weight;
    }

    @Override
    public String toString() {
        return String.format("%d->%d (%d) ", from, to, weight);
    }
}

class DirectedGraph {
    private final int v;
    private int e;
    private List<DirectedEdge>[] neighborhoodLists;

    public DirectedGraph(int v) {
        this.v = v;
        this.e = 0;
        this.neighborhoodLists = (List<DirectedEdge>[]) new List[v];
        for (int i = 0; i < v; i++) {
            neighborhoodLists[i] = new ArrayList<DirectedEdge>();
        }
    }

    public int getNumberOfEdges() {
        return e;
    }

    public int getNumberOfVertices() {
        return v;
    }

    public void addEdge(DirectedEdge edge) {
        neighborhoodLists[edge.from()].add(edge);
        e++;
    }

    public Iterable<DirectedEdge> getNeighborhoodList(int v) {
        return neighborhoodLists[v];
    }
}

public class DijkstraShortestPath {
    private static SessionFactory factory;
    class DistanceToEdge implements Comparable<DistanceToEdge> {
        private final int edge;
        private long distance;

        public DistanceToEdge(int edge, long distance) {
            this.edge = edge;
            this.distance = distance;
        }

        public long getDistance() {
            return distance;
        }

        public void setDistance(long distance) {
            this.distance = distance;
        }

        public int getEdge() {
            return edge;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + getOuterType().hashCode();
            result = prime * result + (int) (distance ^ (distance >>> 32));
            result = prime * result + edge;
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            DistanceToEdge other = (DistanceToEdge) obj;
            if (!getOuterType().equals(other.getOuterType()))
                return false;
            if (distance != other.distance)
                return false;
            if (edge != other.edge)
                return false;
            return true;
        }

        @Override
        public int compareTo(DistanceToEdge param) {
            int cmp = new Long(distance).compareTo(param.getDistance());

            if (cmp == 0) {
                return new Integer(edge).compareTo(param.getEdge());
            }
            return 0;
        }

        private DijkstraShortestPath getOuterType() {
            return DijkstraShortestPath.this;
        }
    }


    private DirectedEdge[] edgeTo;
    private Long[] distanceTo;
    private Queue<DistanceToEdge> priorityQueue;

    public DijkstraShortestPath(DirectedGraph graph, int source) {
        edgeTo = new DirectedEdge[graph.getNumberOfVertices()];
        distanceTo = new Long[graph.getNumberOfVertices()];
        priorityQueue = new PriorityQueue<DistanceToEdge>(
                graph.getNumberOfVertices());

        for (int v = 0; v < graph.getNumberOfVertices(); v++) {
            distanceTo[v] = Long.MAX_VALUE;
        }
        distanceTo[source] = 0L;

        priorityQueue.offer(new DistanceToEdge(source, 0L));

        while (!priorityQueue.isEmpty()) {
            relax(graph, priorityQueue.poll().getEdge());
        }

    }

    private void relax(DirectedGraph graph, int v) {
        for (DirectedEdge edge : graph.getNeighborhoodList(v)) {
            int w = edge.to();

            if (distanceTo[w] > distanceTo[v] + edge.getWeight()) {
                distanceTo[w] = distanceTo[v] + edge.getWeight();
                edgeTo[w] = edge;
                DistanceToEdge dte = new DistanceToEdge(w, distanceTo[w]);

                priorityQueue.remove(dte);
                priorityQueue.offer(dte);
            }
        }

    }

    public long getDistanceTo(int v) {
        return distanceTo[v];
    }

    public boolean hasPathTo(int v) {
        return distanceTo[v] < Long.MAX_VALUE;
    }

    public Iterable<DirectedEdge> getPathTo(int v) {
        Deque<DirectedEdge> path = new ArrayDeque<DirectedEdge>();
        if (!hasPathTo(v)) {
            return path;
        }
        for (DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) {
            path.push(e);
        }
        return path;
    }

   public static void sciezka(){
       factory = HibernateUtil.getSessionFactory();
       Session session = factory.getCurrentSession();
       Transaction transaction = session.beginTransaction();

       String sql = "select count(distinct uuid) as ilosc from Magazyn;";
       SQLQuery query = session.createSQLQuery(sql);

       List results = query.list();
       Integer ilosc_magazynow =  Integer.parseInt(results.get(0).toString());
       DirectedGraph graph = new DirectedGraph(ilosc_magazynow);
    }

}

